﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;

namespace Service
{
    public abstract class BaseWxV3Request
    {
        /// <summary>
        /// 请求路径
        /// </summary>
        public abstract string Url { get; set; }
        /// <summary>
        /// 请求方法
        /// </summary>
        public abstract string Method { get; set; }
        /// <summary>
        /// 小程序AppId
        /// </summary>
        public abstract string AppId { get; set; }
        /// <summary>
        /// 私钥
        /// </summary>
        public abstract string PrivateKey { get; set; }
        /// <summary>
        /// 商家号
        /// </summary>
        public abstract string MerchantId { get; set; }
        /// <summary>
        /// 微信支付证书序列号
        /// </summary>
        public abstract string SerialNo { get; set; }
        /// <summary>
        /// 忽略响应
        /// </summary>
        public abstract bool IgnoreResponse { get; set; }
        /// <summary>
        /// sha256 with rsa2048(在linux或其他服务器无法执行的解决方案:系统调用openssl)
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public virtual string Sign(string message)
        {
            byte[] keyData = Convert.FromBase64String(PrivateKey);
            using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
            using (RSACng rsa = new RSACng(cngKey))
            {
                byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
                return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
            }
        }

        /// <summary>
        /// REQUEST HEAD AUTH
        /// </summary>
        /// <param name="postData"></param>
        /// <returns></returns>
        public virtual string GetAuthorization(string postData)
        {
            postData = Method.ToUpper() == "POST" ? postData : "";
            var uri = new Uri(Url);
            string urlPath = uri.PathAndQuery;
            string nonce = Guid.NewGuid().ToString();
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();

            string message = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", Method, urlPath, timestamp, nonce, postData);
            string signTxt = Sign(message);

            //Authorization和格式
            string authorzationTxt = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"",
                MerchantId,
                nonce,
                timestamp,
                SerialNo,
                signTxt
                );
            return authorzationTxt;
        }
        
        /// <summary>
        /// 处理函数
        /// </summary>
        /// <param name="postData"></param>
        /// <returns></returns>
        public virtual string Handle(string postData)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
            request.Method = Method.ToUpper();
            request.ContentType = "application/json;charset=UTF-8";
            request.UserAgent = "faloo.com/WebRequest";
            request.Accept = "application/json";

            //生成微信支付V3签名
            string Authorization = GetAuthorization(postData);
            request.Headers.Add("Authorization", Authorization);

            if (Method.ToUpper() == "POST")
            {
                byte[] paramJsonBytes = System.Text.Encoding.UTF8.GetBytes(postData);
                request.ContentLength = paramJsonBytes.Length;
                Stream writer;
                try
                {
                    writer = request.GetRequestStream();
                }
                catch (Exception ex)
                {
                    writer = null;
                }
                writer.Write(paramJsonBytes, 0, paramJsonBytes.Length);
                writer.Close();
            }
            HttpWebResponse response;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                response = ex.Response as HttpWebResponse;
            }
            Stream resStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(resStream);
            string text = reader.ReadToEnd();
            
            if (!response.StatusCode.ToString().Equals("OK") && IgnoreResponse == false)
                throw new Exception($"请求异常=>错误编码:{response.StatusCode}");
            return text;
        }
    }

}